﻿#include <svcbox/svc_box_info_inl.h>
#include <thirdparty/fist-log.h>

#include "network_manager.h"
#include "network_client.h"
#include "network_peer.h"
#include "network_conf.h"

using namespace asio;

CNetworkManager::CNetworkManager()
    : m_service(nullptr), m_acceptor(nullptr), m_server_run(nullptr), m_server_running(true)
      , m_cfg_parser(new CNetworkConfParser()), m_check_invalid_connect(0) { }

CNetworkManager::~CNetworkManager()
{
    delete m_server_run;
    delete m_cfg_parser;
    delete m_acceptor;
    delete m_service;
}

bool CNetworkManager::Init()
{
    if (!m_cfg_parser->Load("../config/server/core/network.xml")) {
        return false;
    }
    LOG_INFO("network init begin. listen:{}", service_listen_port());

    m_service = new io_service();
    m_acceptor = new ip::tcp::acceptor(*m_service);
    auto ep = ip::tcp::endpoint(ip::tcp::v4(), service_listen_port());

    m_acceptor->open(ep.protocol());
    m_acceptor->set_option(ip::tcp::acceptor::reuse_address(true));
    m_acceptor->bind(ep);
    m_acceptor->listen();
    CreateAccept();
    Run();
    LOG_INFO("network init finish!");
    return true;
}

void CNetworkManager::Update()
{
    m_cfg_parser->Update();
    if (fist::get_now_ms_time() - m_check_invalid_connect > CHECK_INVALID_CONNECT_TIME) {
        CheckInvalidConnect();
        m_check_invalid_connect = fist::get_now_ms_time();
    }
}

void CNetworkManager::Destroy()
{
    m_server_running = false;
    m_service->post([this] {
        m_acceptor->close();
    });
    for (auto iter = m_nodes.begin(); iter != m_nodes.end(); ++iter) {
        delete iter->second;
        iter = m_nodes.erase(iter);
    }
}

void CNetworkManager::Run()
{
    m_service->run_for(std::chrono::milliseconds(m_cfg_parser->cfg()->tick));
}

void CNetworkManager::CreateAccept()
{
    CNetworkNode* mobile = new CNetworkClient(this, m_service);
    auto& sck = mobile->sock();
    mobile->set_valid(true);
    m_acceptor->async_accept(sck, [=, this]<typename T>(T&& code) {
        mobile->set_valid(code.value() == 0);
        if (code) {
            LOG_ERROR("async_accept error! code:{}, msg:{}", code.value(), code.message());
            return;
        }
        LOG_INFO("new connect ip:{}", mobile->sock().remote_endpoint().address().to_string().c_str());
        mobile->PostRead();
        CreateAccept();
    });
    m_nodes.insert({mobile, mobile});
}

bool CNetworkManager::RegisterRawPackHandle(int type, IRawPackHandlerManager* manager)
{
    if (auto [t, ok] = m_pack_handlers.insert({type, manager}); !ok) {
        LOG_ERROR("raw pack handle exist! type:{}", type);
        return false;
    }
    LOG_INFO("raw pack handler register! type:{}, name:{}", type, manager->name());
    return true;
}

void CNetworkManager::ProcMessage(int type, netlink_t link, const std::string& msg)
{
    if (auto f = m_pack_handlers.find(type); f != m_pack_handlers.end()) {
        f->second->ProcMessage(link, msg);
        return;
    }
    LOG_ERROR("unknow message type! type:{}", type);
}

netlink_t CNetworkManager::AddConnect(const std::string& ip, uint16_t port, uint32_t timeout)
{
    CNetworkPeer* peer = new CNetworkPeer(this, m_service, ip, port);
    peer->Connect();
    m_nodes.insert({peer, peer});
    return peer;
}

int CNetworkManager::SendOneMessage(netlink_t link, const std::string& msg)
{
    if (auto client = m_nodes.find(link); client != m_nodes.end()) {
        return client->second->SendOneMessage(msg);
    }
    return NETWORK_FAIL;
}

const NetworkConf* CNetworkManager::cfg()
{
    return m_cfg_parser->cfg();
}

void CNetworkManager::CheckInvalidConnect()
{
    for (auto iter = m_nodes.begin(); iter != m_nodes.end();) {
        if (iter->second->valid()) {
            ++iter;
            continue;
        }
        if (!iter->second->canDel()) {
            CNetworkPeer* peer = static_cast<CNetworkPeer*>(iter->second);
            peer->Connect();
            ++iter;
            continue;
        }
        iter->second->Destroy();
        delete iter->second;
        iter = m_nodes.erase(iter);
    }
}
